package br.com.skolar.client.framework;

import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import br.com.skolar.client.LockException;
import br.com.skolar.client.events.MessageEvent;
import br.com.skolar.client.view.SkolarMessages;

import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.GWT.UncaughtExceptionHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.user.client.History;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.ScrollPanel;

public abstract class AbstractPlaceManager implements ValueChangeHandler<String> {
	protected EventBus eventBus;
	
	public abstract void go(String token);
	
	@Override
	public void onValueChange(ValueChangeEvent<String> event) {
		go(event.getValue());
	}

	protected AbstractPlaceManager(EventBus eventBus, final SkolarMessages messages) {
		this.eventBus = eventBus;
		
		History.addValueChangeHandler(this);

		if (!GWT.isScript()) {
			GWT.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
				@Override
				public void onUncaughtException(Throwable e) {
					GWT.log("Exceção em Tempo de Compilação! ", e);
					Window.alert("Exceção em Tempo de Compilação! " + e.getMessage());
				}
			});
		}
		eventBus.addHandler(FailureEvent.class, new Event.Handler<FailureEvent>() {
			@Override
			public void onEvent(FailureEvent e) {
				Class c = e.error.getClass();
				String msg = c==LockException.class ?
						"<p>" + messages.lockError() + "</p>" +
						"<p>" + messages.lockErrorRetry() + "</p>": 
						"<p>" + c.getName() + ":</p><p>" + e.error.getMessage() + "</p>";
				showInDialogBox(messages.error(), msg);
			}
		});
		eventBus.addHandler(MessageEvent.class, new Event.Handler<MessageEvent>() {
			@Override
			public void onEvent(MessageEvent e) {
				showInDialogBox(e.title, e.htmlMessage);
			}
		});
		eventBus.addHandler(SuccessEvent.class, new Event.Handler<SuccessEvent>() {
			@Override
			public void onEvent(SuccessEvent e) {
				showInDialogBox("", "<b>" + messages.success() + "</b>");
			}
		});
	}
	
	private void showInDialogBox(String htmlTitle, String htmlMessage) {
		DialogBox d = new DialogBox();
		d.setHTML(htmlTitle);
		d.setGlassEnabled(true);
		d.setAutoHideEnabled(true);
		d.setAnimationEnabled(true);
		d.setWidget(new ScrollPanel(new HTMLPanel(htmlMessage)));
		d.center();
		d.show();
	}

	public static class TokenBuilder {
		private final String token;
		private final Map<String, List<String>> parameters = new LinkedHashMap();

		public TokenBuilder() {
			this.token = "";
		}

		public TokenBuilder(String token) {
			this.token = token;
		}

		public TokenBuilder addParameter(String key, Number... values) {
			int n = values.length;
			String[] aux = new String[n];
			for (int i=0; i<n; i++)
				if (values[i]!=null)
					aux[i] = values[i].toString();
			return addParameter(key, aux);
		}

		public TokenBuilder addParameter(String key, Date... values) {
			int n = values.length;
			String[] aux = new String[n];
			for (int i=0; i<n; i++)
				if (values[i]!=null)
					aux[i] = String.valueOf(values[i].getTime());
			return addParameter(key, aux);
		}

		public TokenBuilder addParameter(String key, String... values) {
			List<String> valuesList = parameters.get(key);
			if (valuesList==null) {
				valuesList = new LinkedList();
				parameters.put(key, valuesList);
			}
			valuesList.addAll(Arrays.asList(values));
			return this;
		}
		
		public Map<String, List<String>> getParametersMap() {
			Map aux = new LinkedHashMap();
			for(Entry<String, List<String>> entry : parameters.entrySet())
				aux.put(entry.getKey(), Collections.unmodifiableList(entry.getValue()));
			return Collections.unmodifiableMap(aux);
		}
		
		public String generateToken() {
			StringBuilder sb = new StringBuilder(token);
			for(Entry<String, List<String>> entry : parameters.entrySet()) {
				String key = entry.getKey();
				List<String> values = entry.getValue();
				for (String v : values)
					sb.append("&").append(key).append("=").append(v);
			}
			return sb.toString();
		}

		@Override
		public String toString() {
			return generateToken();
		}
	}
	
	protected class TokenParser {
		public final String key;
		public final Map<String, LinkedList<String>> parametersMap;

		public TokenParser(String tokenString) {
			String[] split = tokenString.split("&");
			key = split[0];
			
			HashMap<String, LinkedList<String>> params = new HashMap();
			for (int i=1, n=split.length; i<n; i++) {
				String[] key_value = split[i].split("=");
				String key = key_value[0];
				LinkedList<String> values = params.get(key);
				if (values==null) {
					values = new LinkedList<String>();
					params.put(key, values);
				}
				values.add(key_value.length==1 ? "" : key_value[1]);
			}
			parametersMap = Collections.unmodifiableMap(params);
		}
		
		public String getParameter(String name) {
			LinkedList<String> values = parametersMap.get(name);
			return values!=null && values.size()>0 ? values.get(0) : null;
		}

		public Boolean getBoolean(String name) {
			String v = getParameter(name);
			return v!=null && !v.isEmpty() ? new Boolean(v) : null;
		}

		public Integer getInteger(String name) {
			String v = getParameter(name);
			return v!=null && !v.isEmpty() ? new Integer(v) : null;
		}

		public Long getLong(String name) {
			String v = getParameter(name);
			return v!=null && !v.isEmpty() ? new Long(v) : null;
		}

		public Date getDate(String name) {
			String v = getParameter(name);
			return v!=null && !v.isEmpty() ? new Date(new Long(v)) : null;
		}

		public <E extends Enum> E getEnum(String name, E[] values) {
			String v = getParameter(name);
			return v!=null && !v.isEmpty() ? values[new Integer(v)] : null;
		}

		public List<String> getParameterValues(String name) {
			return parametersMap.get(name);
		}
		
		public Long[] getLongArrayValues(String name) {
			List<String> values = parametersMap.get(name);
			int n = values.size();
			Long[] res = new Long[n];
			for (int i=0; i<n; i++) {
				String v = values.get(i);
				res[i] = v.isEmpty() ? null : new Long(v);
			}
			return res;
		}
	}
}